Rustam Khasanshin

Rustam Khasanshin

Smart contract method call in Go

Consider a blockchain method in Go. Suppose you have a contract in the etherium blockchain and you want to call its method.

The main library for dealing with such contracts is https://pkg.go.dev/github.com/ethereum/go-ethereum/ethclient. Let’s take for example my friends’ token contract running on the binance blockchain (also based on ether) and try calling the method to get the total number of tokens.

First, we need to create a client instance to connect to our blockchain and then make requests to it.

import (
    ...
    "github.com/ethereum/go-ethereum/ethclient"
    ...
)

client, err := ethclient.Dial("https://bsc-dataseed.binance.org/")
if err != nil {
    fmt.Println(err)
    return]
}

Now we have to decide how we will interact with the contract.

In javascript, calling the contract method is easy enough - you can load the contract ABI and the web3 library will do everything. In Go, it’s a bit tricky.

In order to call a method in Go, you have to write that method. It makes sense. And the same logic works with contracts. But to rewrite all the methods is long and tedious, so people invented code generation for this contract from its ABI. For this, we need the abigen library. You can get it on your mac via Homebrew.

$ brew tap ethereum/ethereum
$ brew install ethereum

To check if the installation was successful, try to output the abigen version.

$ abigen -v
> abigen version 1.10.25-stable

Abigen successfully installed, time to generate Go code. Go to the contract page in bscscan — https://bscscan.com/address/0xE0b1EF69BC4AB4173989C1190f0d77A813f3B726, and download its ABI.

Contract -> Code -> Contract ABI -> Export ABI -> RAW/Text Format.

Save the file in the programm directory and name it token.abi. Next we call abigen. The --abi parameter specifies our ABI file, the --pkg parameter specifies the name of the package for our Go code, the --type parameter specifies the name of the structure to which all methods will be bound, the --out parameter specifies the name of the Go file.

$ abigen --abi=token.abi --pkg=main --type=token --out=token.go

The file token.go with the code should appear in the directory. It can already be used. But first let’s create an address instance from our string contract address. We need it to create an instance of the token afterwards. This is done using the library github.com/ethereum/go-ethereum/common.

import (
    ...
    "github.com/ethereum/go-ethereum/common"
    ...
)

address := common.HexToAddress("0xE0b1EF69BC4AB4173989C1190f0d77A813f3B726")

Now create the actual token instance itself.

token, err := NewToken(address, client)
if err != nil {
    fmt.Println(err)
    return
}

And try to call a method to get the total number of tokens - TotalSupply. But this method requires passing parameters of special type &bind.CallOpts{}. We don’t want to pass parameters, but we need to pass an empty value. And the type itself can be loaded from library github.com/ethereum/go-ethereum/accounts/abi/bind.

import (
    ...
	"github.com/ethereum/go-ethereum/accounts/abi/bind"
    ...
)

totalSupply, err := token.TotalSupply(&bind.CallOpts{})
if err != nil {
    fmt.Println(err)
    return
}

In general at this stage we can already observe the total number of tokens in totalSupply. But we cannot know whether this token is divisible or not. And it may be that the resulting value may be fractional, but we don’t know where the point is. To understand how much this token is divisible, we need to know its Decimals parameter. For this, we have a special method.

decimals, err := token.Decimals(&bind.CallOpts{})
if err != nil {
    fmt.Println(err)
    return
}

Simply put, the Decimals parameter is the number of digits after the dot. Creating a floating point number and displaying it correctly is worthy of a separate article. So let’s just display the floating point number using what we have.

s := totalSupply.String()
fmt.Println(s[:len(s)-int(decimals)] + "." + s[len(s)-int(decimals):])

That’s pretty much it. We have learned how to connect to the blockchain and call a contract method with a return value. The source code can be found on the github.